using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Drawing.Printing;
using Microsoft.Win32;
using System.Drawing;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Text;

namespace qPrintComponent
{
    public sealed class PrinterInfoQ
    {

        private string _PrinterName;
        private string _PortName;

        internal PrinterInfoQ(string PrinterName, string PortName)
        {
            _PrinterName = PrinterName;
            _PortName = PortName;
        }
        internal PrinterInfoQ(PrinterInfo5 info)
        {
            _PrinterName = info.PrinterName;
            _PortName = info.PortName;
        }

        public override string ToString()
        {
            return PrinterMemory;
        }
        [Description("Retrieves a list of available paper bins.")]
        public string BinNames
        {
            get { return JoinString(sBinNames); }
        }
        [Browsable(false)]
        public StringCollection sBinNames
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.BINNAMES, 48); }
        }
        [Browsable(false)]
        public Collection<int> iBinNames
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.BINS, PtrType.SHORT); }
        }

        [Description(@"Indicates if the printer supports collating.")]
        public bool CanCollate
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.COLLATE); }
        }
        [Description("Gets a value indicating whether this printer is a color device.")]
        public bool CanProcessColor
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.COLORDEVICE); }
        }
        [Description(@"The number of copies the device can print.")]
        public int Copies
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.COPIES); }
        }
        [Description(@"The version number of the printer driver.")]
        public int DriverVersion
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.DRIVER); }
        }

        [Description(@"A value indicating whether the printer supports double-sided printing.")]
        public bool CanDuplex
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.DUPLEX); }
        }
        [Browsable(false)]
        public Collection<int> Resolutions
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.ENUMRESOLUTIONS, PtrType.LONG); }
        }
        public int Fields
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.FIELDS); }
        }
        [Browsable(false)]
        public int Extra
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.EXTRA); }
        }
        [Description(@"Retrieves the names of any additional files that need to be loaded when a driver is installed.")]
        public string PrinterFileDependencies
        {
            get { return JoinString(GetDeviceCapabilities(DeviceCapabilities.FILEDEPENDENCIES, 128)); }
        }
        [Description("Retrieves the names of the paper forms that are currently available for use.")]
        public string MediaReady
        {
            get { return JoinString(GetDeviceCapabilities(DeviceCapabilities.MEDIAREADY, 128)); }
        }
        [Description("Retrieves the names of the supported media types.")]
        public string MediaTypeNames
        {
            get { return JoinString(GetDeviceCapabilities(DeviceCapabilities.MEDIATYPENAMES, 128)); }
        }
        [Browsable(false)]
        public StringCollection MediaNames
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.MEDIATYPENAMES, 128); }
        }
        [Browsable(false)]
        public Collection<int> MediaTypes
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.MEDIATYPES, PtrType.INTEGER); }
        }
        [Browsable(false)]
        public Collection<int> NuMup
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.NUP, PtrType.INTEGER); }
        }
        public string PaperNames
        {
            get { return JoinString(sPaperNames); }
        }
        [Browsable(false)]
        public StringCollection sPaperNames
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.PAPERNAMES, 128); }
        }
        [Browsable(false)]
        public Collection<int> Papers
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.PAPERS, PtrType.SHORT); }
        }
        [Browsable(false)]
        public Collection<Point> PaperSizes
        {
            get { return GetDeviceCapabilitiesPoint(DeviceCapabilities.PAPERSIZE); }
        }
        [Description(@"A list of printer description languages supported by the printer.")]
        public string PrinterLanguages
        {
            get { return JoinString(GetDeviceCapabilities(DeviceCapabilities.PERSONALITY, 64)); }
        }
        [Description(@"Indicates the amount of available printer memory, in kilobytes.")]
        public string PrinterMemory
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.PRINTERMEM).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }
        [Description(@"Indicates the printrate per minute the device can print.")]
        public string PrintRate
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.PRINTRATEPPM).ToString(System.Globalization.CultureInfo.InvariantCulture); }
        }
        [Browsable(false)]
        public int Size
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.SIZE); }
        }

        [Description("A value indicating whether the printer supports stappling.")]
        public bool CanStaple
        {
            get { return GetDeviceCapabilities(DeviceCapabilities.STAPLE); }
        }
        [Description("The specification version to which the printer driver conforms.")]
        public int Version
        {
            get { return GetDeviceCapabilitiesInt(DeviceCapabilities.VERSION); }
        }

        private static string JoinString(StringCollection col)
        {
            if (col.Count == 0)
                return "Unknown";
            StringBuilder b = new StringBuilder();
            foreach (string s in col)
            {
                b.Append(s);
                b.Append(", ");
            }
            if (b.Length > 1)
                b.Length = b.Length - 2;
            return b.ToString();

        }

        internal bool GetDeviceCapabilities(DeviceCapabilities dc)
        {
            return (qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, IntPtr.Zero, IntPtr.Zero) == 1);
        }
        internal int GetDeviceCapabilitiesInt(DeviceCapabilities dc)
        {
            return qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, IntPtr.Zero, IntPtr.Zero);
        }
        internal Collection<int> GetDeviceCapabilities(DeviceCapabilities dc, PtrType type)
        {
            Collection<int> col = new Collection<int>();
            int num1 = qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, IntPtr.Zero, IntPtr.Zero);
            if (num1 <= 0) return col;
            //int num2 = (int)type;
            int num3;
            switch (type)
            {
                case PtrType.SHORT: num3 = 2; break;
                case PtrType.LONG: num3 = 8; break;
                default: num3 = 4; break;
            }
            IntPtr ptr1 = Marshal.AllocCoTaskMem(num3 * num1);
            qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, ptr1, IntPtr.Zero);
            for (int num4 = 0; num4 < num1; num4++)
            {
                switch (type)
                {
                    case PtrType.SHORT:
                        col.Add((int)Marshal.ReadInt16(ptr1, (num3 * num4)));
                        break;
                    case PtrType.INTEGER:
                        col.Add(Marshal.ReadInt32(ptr1, (num3 * num4)));
                        break;
                    case PtrType.LONG:
                        col.Add((int)Marshal.ReadInt64(ptr1, (num3 * num4)));
                        break;
                    default:
                        break;
                }
            }
            Marshal.FreeCoTaskMem(ptr1);
            return col;
        }
        internal StringCollection GetDeviceCapabilities(DeviceCapabilities dc, int length)
        {
            StringCollection col = new StringCollection();
            int num1 = qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, IntPtr.Zero, IntPtr.Zero);
            if (num1 <= 0) return col;
            IntPtr ptr1 = Marshal.AllocCoTaskMem(length * num1);
            qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, ptr1, IntPtr.Zero);
            for (int num3 = 0; num3 < num1; num3++)
            {
                int i = ptr1.ToInt32() + (length * num3);
                IntPtr ptr2 = new IntPtr(i);
                col.Add(Marshal.PtrToStringAuto(ptr2));
            }
            Marshal.FreeCoTaskMem(ptr1);
            return col;
        }
        internal Collection<Point> GetDeviceCapabilitiesPoint(DeviceCapabilities dc)
        {
            Collection<Point> col = new Collection<Point>();
            int num1 = qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, IntPtr.Zero, IntPtr.Zero);
            if (num1 <= 0) return col;
            IntPtr ptr1 = Marshal.AllocCoTaskMem(8 * num1);
            qStatic.DeviceCapabilitiesW(this._PrinterName, this._PortName, (int)dc, ptr1, IntPtr.Zero);
            for (int num3 = 0; num3 < num1; num3++)
            {
                Point p = new Point();
                p.X = Marshal.ReadInt32(new IntPtr(ptr1.ToInt32() + (num3 * 8)));
                p.Y = Marshal.ReadInt32(new IntPtr(ptr1.ToInt32() + (num3 * 8) + 4));
                col.Add(p);
            }
            Marshal.FreeCoTaskMem(ptr1);
            return col;
        }

        internal enum DeviceCapabilities : ushort
        {
            FIELDS = 1,
            PAPERS = 2,
            PAPERSIZE = 3,
            MINEXTENT = 4,
            MAXEXTENT = 5,
            BINS = 6,
            DUPLEX = 7,
            SIZE = 8,
            EXTRA = 9,
            VERSION = 10,
            DRIVER = 11,
            BINNAMES = 12,
            ENUMRESOLUTIONS = 13,
            FILEDEPENDENCIES = 14,
            TRUETYPE = 15,
            PAPERNAMES = 16,
            ORIENTATION = 17,
            COPIES = 18,
            BINADJUST = 19,
            EMF_COMPLIANT = 20,
            DATATYPE_PRODUCED = 21,
            COLLATE = 22,
            MANUFACTURER = 23,
            MODEL = 24,
            PERSONALITY = 25,
            PRINTRATE = 26,
            PRINTRATEUNIT = 27,
            PRINTERMEM = 28,
            MEDIAREADY = 29,
            STAPLE = 30,
            PRINTRATEPPM = 31,
            COLORDEVICE = 32,
            NUP = 33,
            MEDIATYPENAMES = 34,
            MEDIATYPES = 35
        }

        internal enum PtrType
        {
            SHORT = 1,
            INTEGER = 2,
            LONG = 3,
        }
    }
}
